﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Text.RegularExpressions;
using QuestTools.Properties;
using System.Runtime.InteropServices;

namespace QuestTools
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            alphabetWindow = new Alphabet(this);
        }

        #region Members
        Alphabet alphabetWindow;

        public static string rusAlphabet = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
        public static string engAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

        Dictionary< int, string> engTel = new Dictionary<int, string>{ { 2,"abc" },
                                           { 3,"def" },
                                           { 4,"ghi" },
                                           { 5,"jkl" },
                                           { 6,"mno" },
                                           { 7,"pqrs" },
                                           { 8,"tuv" },
                                           { 9,"wxyz" } };

        Dictionary<int, string> rusTel = new Dictionary<int, string>{ { 2, "абвг" },
                                           { 3, "дежз" },
                                           { 4, "ийкл" },
                                           { 5, "мноп" },
                                           { 6, "рсту" },
                                           { 7, "фхцч" },
                                           { 8, "шщъы" },
                                           { 9, "ьэюя" } };

        List<string> streetCache = new List<string>();

        #endregion

        #region Events

        private void startButton_Click(object sender, EventArgs e)
        {
            Process();
        }

        private void maskRdBtn_CheckedChanged(object sender, EventArgs e)
        {
            infoTB.Text = "Поиск по маске: Под маской подразумевается комбинация известных букв искомого слова и специальных символов «*» и «?». «*» - означает любую строку символов, «?» – означает любой одиночный символ.";
        }

        private void similarWordsRdBtn_CheckedChanged(object sender, EventArgs e)
        {
            infoTB.Text = "Поиск схожих названий: поиск по произвольной последовательности букв. Сортировка по максимальной схожести.";
        }

        void symbolInput_TextChanged(object sender, System.EventArgs e)
        {
            GetSymbolsFromAlphabet();
        }

        private void streets_CheckedChanged(object sender, EventArgs e)
        {
            Process();
        }

        private void pereulki_CheckedChanged(object sender, EventArgs e)
        {
            Process();
        }

        private void buildingsNumber_CheckedChanged(object sender, EventArgs e)
        {
            Process();
        }
        
        void bNumbTB_TextChanged(object sender, System.EventArgs e)
        {
            if (buildingsNumber.Checked)
                Process();
        }

        void symbolsTB_TextChanged(object sender, System.EventArgs e)
        {
            GetASCIICodeSymbol();
        }

        void ASCIITB_TextChanged(object sender, System.EventArgs e)
        {
            GetSymbolFromASCII();
        }

        void telSymbolTB_TextChanged(object sender, System.EventArgs e)
        {
            GetTelephonInfo();
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            ASCIITB.Enabled = !ASCIITB.Enabled;
            ASCIITB.ReadOnly = !ASCIITB.ReadOnly;

            symbolsTB.Enabled = !symbolsTB.Enabled;
            symbolsTB.ReadOnly = !symbolsTB.ReadOnly;

            if (ASCIITB.ReadOnly)
                ASCIITB.TextChanged -= new System.EventHandler(ASCIITB_TextChanged);
            else
                ASCIITB.TextChanged += new System.EventHandler(ASCIITB_TextChanged);

            if (symbolsTB.ReadOnly)
                symbolsTB.TextChanged -= new System.EventHandler(symbolsTB_TextChanged);
            else
                symbolsTB.TextChanged += new System.EventHandler(symbolsTB_TextChanged);
        }

        //Show alphabet
        private void button1_Click(object sender, EventArgs e)
        {
            alphabetWindow.Visible = !alphabetWindow.Visible;
            alphabetWindow.Location = new Point(this.Right, this.Location.Y);
            alphabetWindow.Size = Size.Empty;
        }

        //Selecting tab for street intersection
        private void tabControl1_Selecting(object sender, TabControlCancelEventArgs e)
        {
            //Fill street cahce
            if (streetCache.Count != 0)
                return;

            StringReader strReader = new StringReader(Resources.Intersections);
            string line;

            while ((line = strReader.ReadLine()) != null)
            {
                int i = 0;
                string res = string.Empty;
                while (line[i] != ';')
                {
                    res += line[i];
                    i++;
                }
                if (res != string.Empty)
                    streetCache.Add(res);
            }
        }

        //Street intersection tab
        private void listView2_ItemChecked(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            ListViewItem test = new ListViewItem();

            test.SubItems.Add("есть");
            listView3.Items.Add(test);
        }

        private void morseTB_TextChanged(object sender, EventArgs e)
        {
            string[] results = Morse.Calculate(morseTB.Text);
            morseRusTB.Text = results[0];
            morseEnglTB.Text = results[1];
            invertMorseTB.Text = Morse.GetInvertedCode(morseTB.Text);
            results = Morse.Calculate(invertMorseTB.Text);
            morseInvertRusTB.Text = results[0];
            morseInvertEnglTB.Text = results[1];
        }


        protected override void OnMove(EventArgs e)
        {
            base.OnMove(e);
            if (alphabetWindow != null)
                alphabetWindow.Location = new Point(this.Right, this.Location.Y);
        }

        #endregion

        #region Numerical analyzer

        private void GetTelephonInfo()
        {
            string reg = @"(\d{2})\s?";
            Regex codes = new Regex(reg);
            engRaskl.Text = string.Empty;
            russRaskl.Text = string.Empty;

            if (telSymbolTB.Text.Length < 2)
            {
                engRaskl.Text = "Gimme gimme more!";
                russRaskl.Text = "Братюнь, маловато будет!";
            }

            foreach (Match match in codes.Matches(telSymbolTB.Text))
            {
                string str = match.ToString();
                if (str == string.Empty)
                    continue;

                int n = int.Parse(str);
                string res = string.Empty;

                if ((n / 10) < 2)
                {
                    engRaskl.Text += "хз ";
                    russRaskl.Text += "хз ";
                    continue;
                }

                engTel.TryGetValue(n / 10, out res);
                if (res != string.Empty)
                {
                    int second = ((n % 10) % engTel[n / 10].Length) == 0 ? engTel[n / 10].Length : (n % 10) % engTel[n / 10].Length;
                    engRaskl.Text += engTel[n / 10][second - 1] + " ";
                }

                rusTel.TryGetValue(n / 10, out res);
                if (res != string.Empty)
                {
                    int second = ((n % 10) % rusTel[n / 10].Length) == 0 ? rusTel[n / 10].Length : (n % 10) % rusTel[n / 10].Length;
                    russRaskl.Text += rusTel[n / 10][second - 1] + " ";
                }
            }
        }

        private void GetASCIICodeSymbol()
        {
            string reg = @"(\w)";
            Regex codes = new Regex(reg);
            ASCIITB.Text = string.Empty;

            foreach (Match match in codes.Matches(symbolsTB.Text))
            {
                string str = match.ToString();
                if (str == string.Empty)
                    continue;

                int n = Convert.ToInt16(str[0]);

                ASCIITB.Text += n.ToString() + " ";
            }
        }

        private void GetSymbolFromASCII()
        {
            string reg = @"(\d+)\s?";
            Regex codes = new Regex(reg);
            symbolsTB.Text = string.Empty;

            foreach (Match match in codes.Matches(ASCIITB.Text))
            {
                try
                {
                    string str = match.ToString();
                    int n = int.Parse(str);

                    char sym = (char)n;
                    symbolsTB.Text += sym + " ";
                }
                catch (Exception)
                {
                    symbolsTB.Text = "Error";
                }
            }
        }

        private void GetSymbolsFromAlphabet()
        {
            string reg = @"(\d+)\s?";
            Regex numbers = new Regex(reg);
            rusAlphabetTB.Text = string.Empty;
            engAlphabetTB.Text = string.Empty;
            sumTB.Text = string.Empty;

            foreach (Match match in numbers.Matches(symbolInput.Text))
            {
                string str = match.ToString();
                try
                {
                    int n = int.Parse(str);

                    //получить русскую букву
                    if (n < 34 && n > 0)
                        rusAlphabetTB.Text += rusAlphabet[n - 1];
                    else
                        rusAlphabetTB.Text += "Братюнь, я хз шо тут! :(";

                    //получить английскую букву
                    if (n < 27 && n > 0)
                        engAlphabetTB.Text += engAlphabet[n - 1];
                    else
                        engAlphabetTB.Text += "Братиш, не заё!";
                }
                catch (Exception)
                {
                    rusAlphabetTB.Text += "Братюнь, я хз шо тут! :(";
                    engAlphabetTB.Text += "Братиш, не заё!";
                }

                int sum = 0;
                foreach (char c in str)
                {
                    if (c == ' ') continue;

                    string num = string.Empty + c;
                    sum += int.Parse(num);
                }
                rusAlphabetTB.Text += " ";
                engAlphabetTB.Text += " ";
                sumTB.Text += sum.ToString() + " ";
            }
        }

        #endregion

        #region Methods for Streets 

        private void Process()
        {
            listView1.Items.Clear();
            if (maskRdBtn.Checked)
                FindWordsUsingMask();
            else if (similarWordsRdBtn.Checked)
                SimilarWordsFunc();//Find similar words from streets names list
        }

        private string GetLineFromDictionary()
        {
            string resultLine = string.Empty;

            for(int n = 0; n < Resources.Streets.Length; n++)
            {
                if (Resources.Streets[0] == 13)
                {
                    if (resultLine.Length == 0)
                        //пропускаем последовательность пробелов и переносов строк
                        continue;
                    else
                        break;//нашли пробел или перенос и есть уже слово? возвращаем его
                }

                resultLine += (char)Resources.Streets[n];
            }

            return resultLine;
        }

        private void FindWordsUsingMask()
        {
            try
            {
                if (streetInput.Text == string.Empty)
                    return;

                string line,
                    regStringForStreet = streetInput.Text.ToLower().Trim().Replace(@"*", @".*").Replace(@"?", @".").Insert(0, @"\b"),
                    regStringForGroups = @"(?<Type>.*);(?<RusName>.*);(?<UkrName>.*);(?<Numb>.*);(?<Region>.*)";
                regStringForStreet = regStringForStreet.Insert(regStringForStreet.Length, @"$");
                Regex regForStreet = new Regex(regStringForStreet),
                      regForGroups = new Regex(regStringForGroups); ;
                StringBuilder strBld = new StringBuilder();
                int counter = 0;

                StringReader strReader = new StringReader(Resources.Streets);

                while ((line = strReader.ReadLine()) != null)
                //валим по всем словам из файла
                {
                    Match groupsResult = regForGroups.Match(line), res = Match.Empty;

                    if (groupsResult.Groups["RusName"] != Match.Empty)
                        res = regForStreet.Match(groupsResult.Groups["RusName"].ToString().ToLower());
                    
                    if (groupsResult.Groups["UkrName"] != Match.Empty && res == Match.Empty)
                        res = regForStreet.Match(groupsResult.Groups["UkrName"].ToString().ToLower());

                    if (res != Match.Empty)
                        AddNewListItem(groupsResult, ref counter);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void SimilarWordsFunc()
        {
            try
            {
                List<KeyValuePair<Match, double>> resDict = new List<KeyValuePair<Match, double>>();

                int counter = 0;
                string input = streetInput.Text;
                double coeff = double.Parse(coeffTb.Text);
                if (input.Length > 2)
                {
                    string line,
                        regStringForGroups = @"(?<Type>.*);(?<RusName>.*);(?<UkrName>.*);(?<Numb>.*);(?<Region>.*)";
                    Regex regForGroups = new Regex(regStringForGroups);
                    StringReader strReader = new StringReader(Resources.Streets);

                    while ((line = strReader.ReadLine()) != null)
                    //валим по всем словам из файла
                    {
                        Match groupsResult = regForGroups.Match(line);

                        double k = GetJacquardCoefficient(groupsResult.Groups["RusName"].ToString().ToLower(), input.Trim().ToLower());
                        if (k > coeff)
                        //проверяем коэффициент
                        {
                            counter++;
                            resDict.Add(new KeyValuePair<Match, double>(groupsResult, k));
                        }
                    }

                    resDict.Sort(
                        delegate(KeyValuePair<Match, double> first, KeyValuePair<Match, double> second)
                        {
                            return second.Value.CompareTo(first.Value);
                        });

                    ShowResults(resDict);
                }
                else
                    MessageBox.Show("Не достаточно символов!");
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }

        private double GetJacquardCoefficient(string word, string input)
        {
            int a = word.Length,
                b = input.Length,
                c = 0;

            foreach (char sym in input)
            {
                if (word.Contains(sym))
                {
                    char[] buf = new char[1];
                    buf[0] = sym;
                    word = word.Remove(word.IndexOfAny(buf), 1);
                    c++;
                }
            }

            return (double)(2 * c) / (a + b);
        }

        private void ShowResults(List<KeyValuePair<Match, double>> resDict)
        {
            StringBuilder strBld = new StringBuilder();
            int counter = 0;

            foreach (KeyValuePair<Match, double> node in resDict)
            {
                AddNewListItem(node.Key, ref counter);
            }
        }

        private void AddNewListItem(Match mathcGroups, ref int counter)
        {
            if (ApplyFilter(mathcGroups))
            {
                counter++;
                ListViewItem lstItem = new ListViewItem(counter.ToString());
                lstItem.SubItems.Add(mathcGroups.Groups["Type"].ToString());
                lstItem.SubItems.Add(mathcGroups.Groups["RusName"].ToString());
                lstItem.SubItems.Add(mathcGroups.Groups["UkrName"].ToString());
                lstItem.SubItems.Add(mathcGroups.Groups["Numb"].ToString());
                lstItem.SubItems.Add(mathcGroups.Groups["Region"].ToString());
                listView1.Items.Add(lstItem);
            }
        }
        
        private bool ApplyFilter(Match street)
        {
            bool result = false;

            if (!streets.Checked && !pereulki.Checked)
                result = true;
            else if (streets.Checked && street.Groups["Type"].ToString() == "ул")
                result = true;
            else if (pereulki.Checked && street.Groups["Type"].ToString() == "пер")
                result = true;

            if (!buildingsNumber.Checked || (buildingsNumber.Checked && bNumbTB.Text == string.Empty)
                || (street.Groups["Numb"].ToString() == string.Empty))
                return result;

            
            if ((int.Parse(street.Groups["Numb"].ToString()) >= int.Parse(bNumbTB.Text) && result))
                result = true;
            else
                result = false;

            return result;                
        }

        #endregion

        #region Street intersection

        public void IntersectTextChanged(object sender, EventArgs e)
        {
            listView2.Items.Clear();

            if (streetIntersectTB.Text == string.Empty)
                return;

            string regStr = @"\b.*" + streetIntersectTB.Text.ToLower() + @".*$";
            Regex regExp = new Regex(regStr);

            foreach (string street in streetCache)
            {
                if (regExp.Match(street.ToLower()).Success)
                {
                    ListViewItem lstItem = new ListViewItem();
                    string newItem = street;

                    if (street.Contains('z'))
                    {
                        newItem = street.Remove(street.IndexOf('z'), 1);
                        lstItem = new ListViewItem("переулок");
                    }
                    else
                    {
                        lstItem = new ListViewItem("улица");
                    }

                    lstItem.SubItems.Add(newItem);
                    listView2.Items.Add(lstItem);
                }
            }
        }

        #endregion
    }
}
